package com.daffodil.easyfile.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.daffodil.core.constant.Constants;
import com.daffodil.core.dao.JpaDao;
import com.daffodil.core.entity.Query;
import com.daffodil.core.exception.BaseException;
import com.daffodil.easyfile.entity.EasyfileChunk;
import com.daffodil.easyfile.entity.EasyfileEntity;
import com.daffodil.easyfile.service.IEasyfileService;
import com.daffodil.util.DateUtils;
import com.daffodil.util.HqlUtils;
import com.daffodil.util.StringUtils;
import com.daffodil.util.file.FileUploadUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @author yweijian
 * @date 2020年12月24日
 * @version 1.0
 * @description
 */
@Service
@Slf4j
public class EasyfileServiceImpl implements IEasyfileService{
	
	@Autowired
	private JpaDao<String> jpaDao;
	
	@Override
	public List<EasyfileEntity> selectEasyfileEntityList(Query<EasyfileEntity> query) {
		StringBuffer hql = new StringBuffer("from EasyfileEntity where 1=1");
		List<Object> paras = new ArrayList<Object>();
		HqlUtils.createHql(hql, paras, query);
		return jpaDao.search(hql.toString(), paras, EasyfileEntity.class, query.getPage());
	}
	
	@Override
	@Transactional
	public EasyfileChunk easyfileUpload(MultipartFile chunkFile, EasyfileChunk easyfileChunk, String baseDir) {
		//检测分片是否已上传
		Boolean isExist = this.checkChunkIsExist(easyfileChunk);
		if(isExist) {
			return this.easyfileChunkIsExist(easyfileChunk);
		}
		return this.easyfileChunkNoExist(chunkFile, easyfileChunk, baseDir);
	}
	
	@Override
	@Transactional
	public EasyfileEntity easyfileMerge(EasyfileChunk easyfileChunk, String baseDir) {
		EasyfileEntity easyfileEntity = null;
		String fileSuffix = FilenameUtils.getExtension(easyfileChunk.getFileName()).toLowerCase();
		String fileName = FileUploadUtils.encodingFilename(easyfileChunk.getFileName());
		fileName = StringUtils.isNotEmpty(fileSuffix) ? (fileName + "." + fileSuffix) : fileName;
		String filePath = DateUtils.datePath() + File.separator + fileName;
		
		String hql = "from EasyfileChunk where fileId = ? order by chunk asc";
		List<EasyfileChunk> chunks = jpaDao.search(hql, easyfileChunk.getFileId(), EasyfileChunk.class);
		
		if(StringUtils.isNotEmpty(chunks)) {
			Boolean result = this.mergeEasyFileChunk(baseDir, filePath, chunks);
			if(result) {//分片合并成功
				easyfileEntity = new EasyfileEntity();
				easyfileEntity.setFileName(fileName);
				easyfileEntity.setFilePath(filePath);
				easyfileEntity.setStatus(Constants.NORMAL);
				easyfileEntity.setOriginalName(easyfileChunk.getFileName());
				easyfileEntity.setFileSize(easyfileChunk.getFileSize());
				easyfileEntity.setFileSuffix(fileSuffix);
				easyfileEntity.setFileType(easyfileChunk.getFileType());
				jpaDao.save(easyfileEntity);
			}
		}
		return easyfileEntity;
	}

	@Override
	@Transactional
	public void removeEasyfileChunk(EasyfileChunk easyfileChunk) {
		jpaDao.delete("delete from EasyfileChunk where fileId = ?", easyfileChunk.getFileId());
	}

	@Override
	public EasyfileEntity selectEasyfileEntityById(String id) {
		return jpaDao.find(EasyfileEntity.class, id);
	}

	@Override
	@Transactional
	public void deleteEasyfileEntityByIds(String[] ids) {
		if(StringUtils.isNotEmpty(ids)){
			for(String id : ids){
				EasyfileEntity easyfileEntity = jpaDao.find(EasyfileEntity.class, id);
				if(StringUtils.isNotNull(easyfileEntity)) {
					easyfileEntity.setStatus(Constants.DELETED);
					jpaDao.update(easyfileEntity);
				}
			}
		}
	}
	
	/**
	 * 检查分片是否已上传
	 * @param easyfileChunk
	 * @return
	 */
	private Boolean checkChunkIsExist(EasyfileChunk easyfileChunk) {
		String hql = "from EasyfileChunk where id = ? and fileId = ?";
		List<Object> paras = new ArrayList<Object>();
		paras.add(easyfileChunk.getId());
		paras.add(easyfileChunk.getFileId());
		int count = jpaDao.count(hql, paras);
		return count > 0;
	}
	
	/**
	 * 分片已上传
	 * @param easyfileChunk
	 * @return
	 */
	private EasyfileChunk easyfileChunkIsExist(EasyfileChunk easyfileChunk) {
		String hql = "select t from EasyfileChunk t where t.chunk =(select max(c.chunk) from EasyfileChunk c where c.fileId = ?) and t.fileId = ?";
		List<Object> paras = new ArrayList<Object>();
		paras.add(easyfileChunk.getFileId());
		paras.add(easyfileChunk.getFileId());
		EasyfileChunk maxChunk = jpaDao.find(hql, paras, EasyfileChunk.class);
		easyfileChunk.setChunk(maxChunk.getChunk());
		return easyfileChunk;
	}
	
	/**
	 * 分片上传不存在
	 * @param chunkFile
	 * @param easyfileChunk
	 * @param baseDir
	 * @return
	 */
	private EasyfileChunk easyfileChunkNoExist(MultipartFile chunkFile, EasyfileChunk easyfileChunk, String baseDir) {
		String chunkPath = FileUploadUtils.chunkFilename(easyfileChunk.getId());
		try {
			File file = FileUploadUtils.getAbsoluteFile(baseDir, chunkPath);
			chunkFile.transferTo(file);
		} catch (IOException e) {
			String msg = "【" + easyfileChunk.getFileName() + "】第" + (easyfileChunk.getChunk() + 1) + "分片文件上传失败";
			log.error(msg , e);
			throw new BaseException(msg);
		}
		easyfileChunk.setChunkPath(chunkPath);
		jpaDao.save(easyfileChunk);
		return easyfileChunk;
	}
	
	/**
	 * 合并分片文件
	 * @param baseDir
	 * @param filePath
	 * @param chunks
	 * @return true==success; false=failed
	 */
	private Boolean mergeEasyFileChunk(String baseDir, String filePath, List<EasyfileChunk> chunks) {
		RandomAccessFile writer = null, reader = null;
		try {
			File file = FileUploadUtils.getAbsoluteFile(baseDir, filePath);
			writer = new RandomAccessFile(file, "rw");
			
            for (int i = 0; i < chunks.size(); i++) {
            	File chunkFile = FileUploadUtils.getAbsoluteFile(baseDir, chunks.get(i).getChunkPath());
                reader = new RandomAccessFile(chunkFile, "r");
                byte[] b = new byte[1024];
                int line = 0;
                while ((line = reader.read(b)) != -1) {//先读后写
                	writer.write(b, 0, line);
                }
            }
            
            return true;
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		return false;
	}

}
